Scan Management API
This document is based on the following source files:
internal/handlers/scan.go-- Scan handler (scan actions + business settings endpoints)internal/app/routers.go-- Route registrationinternal/services/scan_progress.go-- Scan progress modelinternal/services/fingerprint.go-- Fingerprint computation service
Table of Contents
- Overview
- Scan Operation Endpoints
- Fingerprint Computation Endpoints
- Scan Business Settings Endpoints
- GET/PUT /settings/music-path -- Music Path Configuration
- GET/PUT /settings/scan-playlist-mode -- Playlist Merge Mode
- GET/PUT /settings/scan-auto-create-playlists -- Auto-Create Playlists Switch
- GET/PUT /settings/scan-auto-fingerprint -- Auto Audio-Fingerprint Switch
- GET/PUT /settings/scan-title-source -- Scan Title Source Configuration
- GET/PUT /settings/auto-scan -- Auto Scan Configuration
1. Overview
The scan management module handles the discovery, importing, and directory management of local music files. All endpoints require JWT authentication (BearerAuth), with the base path /api/v1.
ScanHandler carries both the scan action endpoints and the scan-related business settings endpoints (/settings/*), consolidating the business-oriented reads/writes of config keys such as music_path and scan_playlist_mode into a single handler.
2. Scan Operation Endpoints
2.1 POST /scan -- Scan and Import Local Music
Method: POSTPath: /api/v1/scanAuthentication: BearerAuth required
Description: Asynchronously scans the music directory and imports newly discovered music files into the database. Returns immediately; status can be polled via /scan/progress.
Request body:
{
"reimport": false
}| Field | Type | Required | Description |
|---|---|---|---|
reimport | boolean | No | Whether to re-import files that already exist (default false) |
Success response (200):
{
"message": "扫描任务已启动"
}Error responses:
| Status | Description |
|---|---|
| 409 | A scan is already in progress |
| 500 | Failed to start scan |
2.2 GET /scan/progress -- Get Scan Progress
Method: GETPath: /api/v1/scan/progressAuthentication: BearerAuth required
Description: Gets progress information for the current scan task.
Success response (200):
{
"status": "scanning",
"total_files": 1000,
"scanned_files": 500,
"imported_files": 480,
"skipped_files": 15,
"failed_files": 5,
"cleaned_files": 0,
"current_file": "/music/album/track.mp3",
"start_time": "2026-06-12T10:00:00Z",
"end_time": null,
"error": ""
}| Field | Type | Description |
|---|---|---|
status | string | Current status: idle / scanning / importing / creating_playlists / completed / failed / cancelling / cancelled |
total_files | int | Total number of files |
scanned_files | int | Number of scanned files |
imported_files | int | Number of imported files |
skipped_files | int | Number of skipped files (already exist) |
failed_files | int | Number of failed files |
cleaned_files | int | Number of stale files cleaned up |
current_file | string | Path of the file currently being processed |
start_time | string/null | Start time (ISO 8601) |
end_time | string/null | End time (ISO 8601) |
error | string | Error message |
2.3 POST /scan/cancel -- Cancel Scan
Method: POSTPath: /api/v1/scan/cancelAuthentication: BearerAuth required
Description: Cancels the scan task in progress.
Success response (200):
{
"message": "扫描任务已取消"
}Error responses:
| Status | Description |
|---|---|
| 400 | No scan task is in progress |
2.4 GET /scan/directories -- Get Subdirectory List
Method: GETPath: /api/v1/scan/directoriesAuthentication: BearerAuth required
Description: Returns the list of first-level subdirectories under the specified path, used for lazy loading of the directory tree. When path is empty, returns the subdirectories under the music root directory.
Query parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | No | Directory path (uses the music root directory when empty) |
Success response (200):
{
"directories": ["/music/pop", "/music/rock"],
"root": "/music"
}Error responses:
| Status | Description |
|---|---|
| 400 | The path must be under the music directory (prevents directory traversal attacks) |
| 500 | Failed to read directory |
2.5 GET /scan/dir-names -- Get All Directory Names
Method: GETPath: /api/v1/scan/dir-namesAuthentication: BearerAuth required
Description: Recursively collects all unique directory names under the music directory, returned in alphabetical order, used for autocompletion of excluded directory names.
Success response (200):
{
"names": ["@eaDir", "Classical", "Pop", "Rock", "tmp"]
}Error responses:
| Status | Description |
|---|---|
| 500 | Failed to collect directory names |
3. Fingerprint Computation Endpoints
3.1 GET /scan/fingerprints/status -- Get Fingerprint Computation Status
Method: GETPath: /api/v1/scan/fingerprints/statusAuthentication: BearerAuth required
Description: Returns ffmpeg chromaprint availability and local song fingerprint computation statistics.
Success response (200):
{
"chromaprint_available": true,
"total": 1000,
"computed": 795,
"missing": 200,
"failed": 5,
"auto_enabled": false
}| Field | Type | Description |
|---|---|---|
chromaprint_available | boolean | Whether ffmpeg chromaprint is available |
total | int | Total number of local songs |
computed | int | Number of songs with computed fingerprints |
missing | int | Number of songs never attempted yet (= total - computed - failed) |
failed | int | Number of songs attempted but failed (no audio track / corrupted / timed out). Never retried automatically — only recompute_all=true tries again |
auto_enabled | boolean | Whether fingerprints are computed automatically after a scan (config scan_auto_fingerprint, default false) |
3.2 POST /scan/fingerprints -- Trigger Batch Fingerprint Computation
Method: POSTPath: /api/v1/scan/fingerprintsAuthentication: BearerAuth required
Description: Asynchronously computes audio fingerprints for local songs; requires ffmpeg with chromaprint support. If a task is already running, it is interrupted and restarted. Each file samples only its first 120 seconds (the AcoustID standard), concurrency adapts to the CPU (clamp(GOMAXPROCS/4, 1, 4)), and failed songs are marked as "attempted" so they are not retried automatically.
Request body:
{
"recompute_all": false
}| Field | Type | Required | Description |
|---|---|---|---|
recompute_all | boolean | No | When true, clears existing fingerprints and the "attempted" markers, then recomputes everything (default false, computes only songs never attempted). This is the only way to retry failed items |
Success response (200):
{
"status": "started",
"total": 200
}Error responses:
| Status | Description |
|---|---|
| 400 | ffmpeg chromaprint is unavailable |
3.3 GET /scan/fingerprints/progress -- Get Fingerprint Computation Progress
Method: GETPath: /api/v1/scan/fingerprints/progressAuthentication: BearerAuth required
Description: Queries the progress of the current fingerprint computation task.
Success response (200):
{
"status": "running",
"computed": 150,
"total": 200,
"failed": 3
}| Field | Type | Description |
|---|---|---|
status | string | Task status: idle / running / done / cancelled |
computed | int | Number computed |
total | int | Total number of tasks |
failed | int | Number failed |
3.4 POST /scan/fingerprints/cancel -- Interrupt Fingerprint Computation
Method: POSTPath: /api/v1/scan/fingerprints/cancelAuthentication: BearerAuth required
Description: Stops the running batch fingerprint computation task and kills its ffmpeg child processes. The fingerprint task is not attached to the scan's cancel channel (that channel is already closed once the scan reports "completed"), so it needs a dedicated cancel entry point. Fingerprints already computed are kept; interrupted songs are not marked as "attempted" and will be computed next time.
Success response (200):
{
"cancelled": true
}| Field | Type | Description |
|---|---|---|
cancelled | boolean | Whether a running task was actually interrupted (false when none is running; not an error) |
4. Scan Business Settings Endpoints
The following endpoints are business-oriented configuration interfaces that provide strongly-typed JSON, come with built-in defaults, and trigger side effects inline after PUT. They write to the same config row as the generic /configs/{key} interface, but frontend business features should always go through these endpoints.
4.1 GET/PUT /settings/music-path -- Music Path Configuration
Path: /api/v1/settings/music-pathAuthentication: BearerAuth required
GET -- Get music path and scan exclusion configuration
Success response (200):
{
"path": "music",
"exclude_dirs": ["@eaDir", "tmp"],
"exclude_paths": []
}| Field | Type | Description |
|---|---|---|
path | string | Music directory path (default "music") |
exclude_dirs | string[] | List of excluded directory names |
exclude_paths | string[] | List of excluded specific paths |
PUT -- Update music path and scan exclusion configuration
After writing the configuration, asynchronously triggers a Scanner rebuild + cleanup of songs in excluded directories (the side effect is consistent with the admin /configs/music_path PUT).
Request body: Same format as the GET response. path cannot be empty.
Error responses:
| Status | Description |
|---|---|
| 400 | Bad request format or empty path |
| 500 | Failed to save configuration |
4.2 GET/PUT /settings/scan-playlist-mode -- Playlist Merge Mode
Path: /api/v1/settings/scan-playlist-modeAuthentication: BearerAuth required
Controls the directory merge mode when automatically creating playlists after a scan. Default directory.
GET response / PUT request body:
{
"mode": "directory"
}| Field | Type | Allowed values | Description |
|---|---|---|---|
mode | string | directory / top_level / bubble_up | directory: generate an independent playlist for each folder (default); top_level: merge playlists by first-level subdirectory; bubble_up: songs appear simultaneously in the playlists of all parent folders |
PUT error responses:
| Status | Description |
|---|---|
| 400 | Bad request format or illegal mode value (only the three enum values above are accepted) |
| 500 | Failed to save configuration |
4.3 GET/PUT /settings/scan-auto-create-playlists -- Auto-Create Playlists Switch
Path: /api/v1/settings/scan-auto-create-playlistsAuthentication: BearerAuth required
Controls whether playlists are automatically created based on the music directory structure after a scan completes. Enabled by default (true). When disabled, scanning only imports songs into the database and no longer auto-creates playlists.
GET response / PUT request body:
{
"enabled": true
}4.4 GET/PUT /settings/scan-auto-fingerprint -- Auto Audio-Fingerprint Switch
Path: /api/v1/settings/scan-auto-fingerprintAuthentication: BearerAuth required
Controls whether chromaprint audio fingerprints are computed automatically for local songs that lack them after a scan completes. Off by default (false): fingerprints only serve "Duplicate detection" and plugin lyric/cover search, making them an on-demand feature, and computing them library-wide keeps the CPU busy long after the scan progress already reads "completed" (songloft-org/songloft#323). While off, users can trigger POST /scan/fingerprints manually from the duplicate detection page.
Once enabled, per-file cost is constant (only the first 120 seconds are sampled), concurrency adapts to the CPU, failures are attempted only once, and the task can be stopped at any time via POST /scan/fingerprints/cancel.
GET response / PUT request body:
{
"enabled": false
}4.5 GET/PUT /settings/scan-title-source -- Scan Title Source Configuration
Path: /api/v1/settings/scan-title-sourceAuthentication: BearerAuth required
Configures the source of the song title during scanning. After switching, a scan in "re-import" mode is required for it to take effect. Triggers a Scanner rebuild after PUT completes.
GET response / PUT request body:
{
"title_source": "tag"
}| Field | Type | Allowed values | Description |
|---|---|---|---|
title_source | string | tag / filename | tag: prefer the title in the audio tags (default); filename: always use the file name (without extension) |
PUT error responses:
| Status | Description |
|---|---|
| 400 | Bad request format or invalid title_source value |
| 500 | Failed to save configuration |
4.6 GET/PUT /settings/auto-scan -- Auto Scan Configuration
Path: /api/v1/settings/auto-scanAuthentication: BearerAuth required
Configures the enabled state and scan interval of auto scan. Takes effect immediately after an update (no restart required), asynchronously triggering the auto-scan scheduler to reconfigure.
GET response / PUT request body:
{
"enabled": false,
"interval_seconds": 3600
}| Field | Type | Description |
|---|---|---|
enabled | boolean | Whether to enable auto scan (default false) |
interval_seconds | int | Scan interval in seconds (default 3600, valid range 60-86400) |
PUT error responses:
| Status | Description |
|---|---|
| 400 | Bad request format or interval_seconds not in the range 60-86400 |
| 500 | Failed to save configuration |
Section source: internal/handlers/scan.go / internal/app/routers.go / internal/services/scan_progress.go / internal/services/fingerprint.go
